package ac.uk.qmul.gemom.scenario;

import java.util.Arrays;
import java.util.Iterator;
import java.util.Set;
import java.util.Vector;
import java.util.Hashtable;
import java.util.Enumeration;


import com.qsphere.boundary.gemom.GEMOMData;
 

/*
* COPYRIGHT DISCLAIMER
* Synopsis: All the source codes presented in this document are intellectual
* properties of Queen Mary, University of London.
* 
* @year 2010
* @author Peng Jiang, John Bigham, Jiayi Wu Jinfu Wang------------QMUL
* @version 1.0 
*/

public class DelayScheduler extends Thread implements Scheduler{
	
	private double unitTime;   // in milli-seconds
	private long currentTimeSlot;
	private double currentElapsedTime;  // in milli-seconds
	private Hashtable<String, TaskContent> queue;
	
	private Object[] keysArray;
	
	private boolean showCounter = true;
	
	private boolean non_stop = true;
	
	private FaultManager faultManager;
	
	private String [] code = {"mean_delay", "delay_distribution"};
	
	public DelayScheduler(FaultManager fm){
		faultManager = fm;
		non_stop = true;
		currentTimeSlot = 0;
		unitTime = 100;
		queue = new Hashtable<String, TaskContent>();
		
	}
	
	public void turnOff(){
		this.non_stop = false;
		
	}
	
	public void enableCounter(boolean show){
		this.showCounter = show;
		
	}
	
	public void reset(){
		this.non_stop = true;
		currentTimeSlot = 0;
		unitTime = 100;
		queue.clear();
		keysArray = null;
	}
	
	public boolean match(GEMOMData data){
		boolean ok = true;
		for(int i = 0; i < code.length; i++){
			if(!data.containsKey(code[i])){
				ok = false;
				break;
			}
		}
			
	    return ok;
	}
	
	public void registerEvent(String ns, String topic, GEMOMData data){
		if(match(data))
		{
		double mean_delay = Double.parseDouble(data.get("mean_delay").toString());
		String delay_distribution = data.get("delay_distribution").toString();
		TaskContent content = new TaskContent();
		if(delay_distribution.equalsIgnoreCase("exp") && mean_delay>0)
		    content.delay = Util.getDelayExpRnd(mean_delay, Math.random());
		else if(delay_distribution.equalsIgnoreCase("const"))
			content.delay = mean_delay;
		else
			content.delay = mean_delay;
		
		content.time = content.delay*1000 + currentElapsedTime;
		content.namespace = ns;
		content.topic = topic;
		if(showCounter){
		    data.put("delay", ""+content.delay);
		    data.put("FM clock time", ""+content.time);
		    data.put("First publish time", ""+currentElapsedTime);
		}
		content.data = data;
		queue.put(""+content.time, content);
		
		schedule();
		}
		
	}
	
	public void schedule(){
		keysArray = null;
		keysArray = new Object[queue.size()];
		
		Set<String> keys = queue.keySet();

		int i = 0;
		Iterator<String> it = keys.iterator();
		while (it.hasNext()) {
			keysArray[i] = it.next();
			i++;
		}
		
		if (keysArray != null && keysArray.length > 0
				&& keysArray[0] instanceof Comparable) {
			Arrays.sort(keysArray);
		}
		
	}
	
	public double getFirstEventTime(){
		if(keysArray != null && keysArray.length > 0)
		    return Double.parseDouble(keysArray[0].toString());
		else
			return -1;
	}
	
	
	public void happen(){
		String key = keysArray[0].toString();
		//double keyInDouble = Double.parseDouble(key);
		TaskContent content  = (queue.get(key));
		if(content != null){
		    try{
			    this.faultManager.onFaultManagerPublish(content.namespace, content.topic, content.data);
			    queue.remove(key);
			}
			catch(Exception e){
				System.err.println(e);
			}
		}
		
	
	}
	
	private boolean shouldHappen(double time){
		if(time <= currentElapsedTime)
		    return true;
		else
			return false;
		    	
	}
	
	public void run(){
		 
		while(non_stop){
			//debugOutput();
			double time = getFirstEventTime();
			//this.faultManager.printLineInMonitor("$$$ curren time = " +currentElapsedTime);
			//this.faultManager.printLineInMonitor("$$$ time = " + time );
			// only does so when there is event in the queue
			if(time>=0 && shouldHappen(time)){
				happen();
			}
			
			currentTimeSlot ++;
			currentElapsedTime += unitTime;
			try{
			    this.sleep((int)unitTime);
			}
			catch(Exception e){
				System.err.println(e);
			}
		}
		
	}
	
	private void debugOutput(){
		
		if (keysArray != null && keysArray.length > 0) {
			
			for(int i = 0; i < keysArray.length; i++)
				this.faultManager.printLineInMonitor("$$$ key = "+keysArray[i].toString());
		}
	}

}

class TaskContent{
	TaskContent(){
	}
//	the parameters in a time segment 
	double delay;
	double time;
	public String namespace;
	public String topic;
	public GEMOMData data;
	
}
